/*
 * Criptografando arquivos com XOR
 * @utor: soneca
 *
 * +--------------------------------------------+
 * | ATENÇÃO: Para coisas importantes, use algo |
 * | mais seguro como criptografia PGP          |
 * +--------------------------------------------+
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openssl/sha.h>
#include <termios.h>

#define PREFIXO "."
#define SUFIXO ".sha512"


// Remove quebra de linha final do fgets()
void rm_nova_linha(char *str){
	if(str[strlen(str)-1]=='\n')
		str[strlen(str)-1]='\0';
}

// Escreve a hash em arquivo
void escreve_hash(char *nome_arq, char *hash){
	FILE *hashf;

	hashf=fopen(nome_arq, "w+");
	fputs(hash, hashf);
}

void echo_off(){
	struct termios term;
	tcgetattr(fileno(stdin), &term);
	term.c_lflag &= ~ECHO;
	tcsetattr(fileno(stdin), TCSANOW, &term);
}
void echo_on(){
	struct termios term;
	tcgetattr(fileno(stdin), &term);
	term.c_lflag |= ECHO;
	tcsetattr(fileno(stdin), TCSANOW, &term);
}

// Compara hash do arquivo com a da senha tentada
int confere_hash(char *nome_arq, char *hash){
	FILE *hashf;
	char linha[200];

	hashf=fopen(nome_arq, "r");
	fgets(linha, 200,hashf);
	rm_nova_linha(linha);

	if( strcmp(hash, linha)==0 ){
		printf("[\e[32mINFO\e[0m] Senha correta\n");
		return 1;
	}else{
		printf("[\e[31mINFO\e[0m] Senha incorreta\n");
		return 0;
	}
}

int main(int argc, char *argv[]){
	FILE *arq, *novo_arq;
	int *dados, cont=0;
	char *key, *ckey, *fname, *hfname, hash[129]="\0", hex[3];
	unsigned char hash_data[SHA512_DIGEST_LENGTH];

	// Gerenciando Argumentos
	if(argc!=2){
		fprintf(stderr, "Uso: %s <arquivo>\n", argv[0]);
		return -1;
	}
	dados=malloc(1);
	memset(dados, 0, 1);
	key=malloc(100);
	memset(key, 0, 100);
	ckey=malloc(100);
	memset(ckey, 0, 100);

	// Memória para os nomes dos arquivos
	fname=malloc( sizeof(char)*strlen(argv[1]) );
	hfname=malloc( sizeof(char)*(strlen(argv[1])+strlen(PREFIXO)+strlen(SUFIXO)) );

	// Nome dos arquivos
	strcpy(fname, argv[1]);
	sprintf(hfname, "%s%s%s", PREFIXO, fname, SUFIXO);

	// Prompt de senha
	printf("[\e[33mAVIS\e[0m] Digite a senha: ");
	echo_off(); scanf("%s", key); echo_on(); putchar('\n');
	if( access(hfname, F_OK)!=0 ){
		printf("[\e[33mAVIS\e[0m] Confirme a senha: ");
		echo_off(); scanf("%s", ckey); echo_on(); putchar('\n');

		if( strcmp(key, ckey)!=0 ){
			printf("[\e[31mERRO\e[0m] As senhas não coincidem\n");
			return -1;
		}
	}

	// Abre e verifica arquivos
	arq=fopen(fname, "rb");
	novo_arq=fopen(".temp", "wb+");

	if(arq==NULL || novo_arq==NULL){
		printf("[\e[31mERRO\e[0m] Problema ao abrir arquivo.\n");
		return -1;
	}

	SHA512( (unsigned char*) key, strlen(key), hash_data );

	for(int k=0; hash_data[k]!='\0' && k<64;k++){
		sprintf(hex, "%02hhx", hash_data[k]);
		strcat(hash, hex);
	}

	if( access(hfname, F_OK)!=-1 ){
		printf("[\e[32mINFO\e[0m] Arquivo com hash encontrado \e[33m%s\e[0m\n", hfname);
		if( confere_hash(hfname, hash)==0 ) //senha incorreta
			return 0; //senha correta
		else
			remove(hfname);
	}else{
			printf("[\e[32mINFO\e[0m] Criando arquivo de hash \e[33m%s\e[0m\n", hfname);
			printf("[\e[33mAVIS\e[0m] Não mude o nome do arquivo até que ele seja descriptografado.\n");
			escreve_hash(hfname, hash);
	}

	printf("[\e[32mINFO\e[0m] Usando cifra em \e[33m%s\e[0m\n", fname);
	setbuf(stdout, NULL);
	while( (!feof(arq)) & fread(dados,1, 1,arq) ){
		*dados=*dados^key[ cont++ % strlen(key) ];
		fwrite(dados, 1, 1, novo_arq);
	}

	// verifica se foi até o fim
	if( feof(arq) )
		printf("[\e[32mINFO\e[0m] Processado com sucesso.\n");
	else
		printf("[\e[31mERRO\e[0m] Há algo errado que não está certo.\n");
	
	remove(argv[1]);
	rename(".temp", argv[1]);
	free(dados);
	fclose(arq);
	fclose(novo_arq);
	return 0;
}
